package org.manon.json.asm;

import java.io.OutputStream;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.Set;

import org.manon.json.SerializerProvider;
import org.manon.json.Setter;
import org.manon.json.filter.Filter;
import org.manon.json.io.InputReader;
import org.manon.json.meta.Entity;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;


/**
 * @author <a href="mailto:jacques.militello@gmail.com">Jacques Militello</a>
 */
public final class ASMUtil {

	/**
	 * Different types.
	 */
	public static final Type TYPE_VOID = Type.getType(void.class);
	public static final Type TYPE_STRING = Type.getType(String.class);
	public static final Type TYPE_OBJECT = Type.getType(Object.class);
	public static final Type TYPE_INT = Type.getType(int.class);
	public static final Type TYPE_BOOLEAN = Type.getType(boolean.class);
	public static final Type TYPE_BYTE_ARRAY = Type.getType(byte[].class);
	public static final Type TYPE_SERIALIZER_PROVIDER = Type.getType(SerializerProvider.class);
	public static final Type TYPE_OUTPUTSTREAM = Type.getType(OutputStream.class);
	public static final Type TYPE_FILTER = Type.getType(Filter.class);
	public static final Type TYPE_SET = Type.getType(Set.class);
	public static final Type TYPE_MAP = Type.getType(Map.class);
	public static final Type TYPE_INPUT_READER = Type.getType(InputReader.class);
	public static final Type TYPE_SETTER = Type.getType(Setter.class);
	public static final Type TYPE_STRING_BUILDER = Type.getType(StringBuilder.class);
	
	
	public static final int JAVA_VERSION;
	static {
		final String javaVersion = System.getProperty("java.version"); 
		if (javaVersion.startsWith("1.5.")) {
			JAVA_VERSION = Opcodes.V1_5;
		} else if (javaVersion.startsWith("1.6.")) {
			JAVA_VERSION = Opcodes.V1_6;
		} else if (javaVersion.startsWith("1.7.")) {
			JAVA_VERSION = Opcodes.V1_7;
		} else {
			throw new IllegalStateException("Java version [" + javaVersion + "] is not supported.");
		}
	}
	
	private ASMUtil(){}
	
	public static String getDescriptor(String classname) {
		return new StringBuilder(64).append("L").append(classname).append(";").toString();
	}
	
	public static String getDescriptorForSerializer(Entity entity) {
		return new StringBuilder(64).append("L").append(entity.getInternalClassNameForSerializer()).append(";").toString();
	}
	
	public static String getDescriptorForDeSerializer(Entity entity) {
		return new StringBuilder(64).append("L").append(entity.getInternalClassNameForDeserializer()).append(";").toString();
	}
	
	public static String getSignature(Class<?> parent,Class<?> generic) {
		return new StringBuilder(64).append("L").append(Type.getInternalName(parent)).append("<").append(Type.getDescriptor(generic)).append(">;")
				.toString();
	}
	
	public static String getSignature(Type parent,Type generic) {
		return new StringBuilder(64).append("L").append(parent.getInternalName()).append("<").append(generic.getInternalName()).append(">;")
				.toString();
	}
	
	public static String getSignature(Class<?> parent,Class<?> generic1,Class<?> generic2) {
		return new StringBuilder(128).append("L").append(Type.getInternalName(parent)).append("<").append(Type.getDescriptor(generic1))
				.append(",").append(Type.getDescriptor(generic2)).append(">;").toString();
	}
	
	public static String getSignature(Type parent,Type generic1,Type generic2) {
		return new StringBuilder(128).append("L").append(parent.getInternalName()).append("<").append(generic1.getInternalName())
				.append(",").append(generic2.getInternalName()).append(">;").toString();
	}
	
	public static int protectedStaticFinal() {
		return Opcodes.ACC_PROTECTED  + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL;
	}
	
	public static int publicStaticFinal() {
		return Opcodes.ACC_PUBLIC  + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL;
	}
	
	public static int privateStaticFinal() {
		return Opcodes.ACC_PRIVATE  + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL;
	}
	
	
	public static boolean isSingleValue(final Class<?> keyClass) {
		return String.class.equals(keyClass) || Enum.class.isAssignableFrom(keyClass) 
				|| Number.class.isAssignableFrom(keyClass)
				|| Date.class.isAssignableFrom(keyClass) 
				|| Timestamp.class.isAssignableFrom(keyClass) 
				|| Calendar.class.isAssignableFrom(keyClass)
				|| keyClass.isPrimitive();
	}
	
	public static boolean isNumberValue(final Class<?> keyClass) {
		return  (keyClass.isPrimitive() && (!keyClass.equals(char.class) && !keyClass.equals(boolean.class))) ||
				Number.class.isAssignableFrom(keyClass) || 
				Date.class.isAssignableFrom(keyClass) ||
				Timestamp.class.isAssignableFrom(keyClass) ||
				Calendar.class.isAssignableFrom(keyClass);
	}
	
}